#!/usr/bin/env python 
# -*- coding:utf-8 -*-

import os
from abc import ABC

import cartopy.crs as ccrs
import matplotlib.colors as colors
import matplotlib.pyplot as plt
import numpy as np
import shapefile
from matplotlib.patches import PathPatch
from matplotlib.path import Path
from scipy.interpolate import Rbf
from cartopy.io.shapereader import Reader
from tornado.web import RequestHandler
import json
import common as com
from logHandler import getLogger
from timeoutHandler import set_timeout



class ColorMapDto(object):
    def __init__(self, value_list, lon_list, lat_list, image_path, image_name, shp_path, city_shp_path, level_list, color_map,
                 min_lon, max_lon, min_lat, max_lat, shp_encoding, show_text, text_list,
                 text_lat_list, text_lon_list, show_river, river_shp, font, show_county_boundary,
                 county_boundary_shp):
        self.value_list = value_list
        self.lon_list = lon_list
        self.lat_list = lat_list
        self.image_path = image_path
        self.image_name = image_name
        self.shp_path = shp_path
        self.city_shp_path = city_shp_path
        self.level_list = level_list
        self.color_map = color_map
        self.min_lon = min_lon
        self.max_lon = max_lon
        self.min_lat = min_lat
        self.max_lat = max_lat
        self.shp_encoding = shp_encoding
        self.show_text = show_text
        self.text_list = text_list
        self.text_lat_list = text_lat_list
        self.text_lon_list = text_lon_list
        self.show_river = show_river
        self.river_shp = river_shp
        self.font = font
        self.show_county_boundary = show_county_boundary
        self.county_boundary_shp = county_boundary_shp


def json_tp_dto(d):
    return ColorMapDto(d["valueList"], d["lonList"], d["latList"], d["imagePath"], d["imageName"], d["shpPath"],
                       d["cityShpPath"], d["levelList"], d["colorMap"], d["minLon"], d["maxLon"], d["minLat"],
                       d["maxLat"], d["shpEncoding"], d["showText"], d["textList"], d["textLatList"],
                       d["textLonList"], d["showRiver"], d["riverShp"], d["font"], d["showCountyBoundary"],
                       d["countyBoundaryShp"])


class ColorMapHandler(RequestHandler, ABC):
    @set_timeout(com.timeoutTime)
    def post(self):
        try:
            json_byte = self.request.body
            json_str = json_byte.decode("utf-8")
            color_map_dto = json.loads(json_str, object_hook=json_tp_dto)
            log = getLogger()
            log.info("开始执行svg出图：{}".format(color_map_dto.__dict__))

            # 用来正常显示中文 SimHei
            plt.rcParams['font.sans-serif'] = [color_map_dto.font]
            # 用来正常显示负号
            plt.rcParams['axes.unicode_minus'] = False

            # 读取txt文件
            lat = color_map_dto.lat_list
            lon = color_map_dto.lon_list
            val = color_map_dto.value_list
            min_lon = color_map_dto.min_lon
            max_lon = color_map_dto.max_lon
            min_lat = color_map_dto.min_lat
            max_lat = color_map_dto.max_lat
            lon_length = int((max_lon - min_lon) * 10) + 1
            lat_length = int((max_lat - min_lat) * 10) + 1

            shp_name = color_map_dto.shp_path

            # 经纬度处理
            olon = np.linspace(min_lon, max_lon, lon_length)
            olat = np.linspace(min_lat, max_lat, lat_length)

            # 转成地理经纬度
            olon, olat = np.meshgrid(olon, olat)

            # 插值处理
            func = Rbf(lon, lat, val, function='linear')
            rhu_data_new = func(olon, olat)

            # 自定义色卡处理
            levels = color_map_dto.level_list
            color_map = color_map_dto.color_map
            under_color = color_map[0]
            over_color = color_map[len(color_map) - 1]
            my_cmap = colors.ListedColormap(color_map)
            my_cmap.set_under(under_color)
            my_cmap.set_over(over_color)
            norm3 = colors.BoundaryNorm(levels, my_cmap.N)

            # 投影方式
            projection = ccrs.Mercator()

            # 创建图实例 创建设置像素
            fig = plt.figure(dpi=300)
            ax = fig.add_subplot(111, projection=projection)

            # 设定要显示的边界区域
            ax.set_extent([min_lon, max_lon, min_lat, max_lat], crs=projection)

            if color_map_dto.show_county_boundary:
                # 如果是重庆不会进这个if，只有省+市才会进来
                if color_map_dto.city_shp_path:
                    # ====================开始叠加市信息=====================
                    ax.add_geometries(Reader(color_map_dto.city_shp_path).geometries(), ccrs.Mercator(),
                                      facecolor='none',
                                      edgecolor='#000000', linewidth=0.8)
                    # ====================叠加市信息结束=====================
                # ====================开始叠加区县信息=====================
                ax.add_geometries(Reader(color_map_dto.county_boundary_shp).geometries(), ccrs.Mercator(),
                                  facecolor='none',
                                  edgecolor='#000000', linewidth=0.1, alpha=0.2)
                # 区域外边界
                ax.add_geometries(Reader(shp_name).geometries(), ccrs.Mercator(), facecolor='none',
                                  edgecolor='#00A2D5', linewidth=1.2)
                # ====================叠加区县信息结束=====================

            if color_map_dto.show_river:
                # ====================开始叠加河流信息=====================
                ax.add_geometries(Reader(color_map_dto.river_shp).geometries(), ccrs.Mercator(),
                                  facecolor='none',
                                  edgecolor='#0063BF', linewidth=0.8)
                # ====================叠加河流信息结束=====================

            if color_map_dto.show_text:
                # ====================开始叠加站号信息=====================
                text_list = color_map_dto.text_list
                text_lat_list = color_map_dto.text_lat_list
                text_lon_list = color_map_dto.text_lon_list
                for i in range(len(text_list)):
                    ax.text(text_lon_list[i], text_lat_list[i], text_list[i], fontsize=5, color="black")
                # ====================叠加站号信息结束=====================

            # 等值面处理
            cs = ax.contourf(olon, olat, rhu_data_new, levels=levels, cmap=my_cmap, norm=norm3, extend='both',
                             transform=projection, alpha=1)

            # 读取shp文件，获取要裁剪的区域clip(环流图不用裁剪)
            if shp_name:
                sf = shapefile.Reader(shp_name, encoding=color_map_dto.shp_encoding)
                for shp_record in sf.shapeRecords():
                    vertices = []
                    codes = []
                    pts = shp_record.shape.points
                    prt = list(shp_record.shape.parts) + [len(pts)]
                    for i in range(len(prt) - 1):
                        for j in range(prt[i], prt[i + 1]):
                            vertices.append((pts[j][0], pts[j][1]))
                        codes += [Path.MOVETO]
                        codes += [Path.LINETO] * (prt[i + 1] - prt[i] - 2)
                        codes += [Path.CLOSEPOLY]
                    clip = Path(vertices, codes)
                    clip = PathPatch(clip, transform=ax.transData)

                # 裁剪显示区域
                for contour in cs.collections:
                    contour.set_clip_path(clip)

            # cartopy 设置背景透明及去除边框
            ax.outline_patch.set_visible(False)
            ax.background_patch.set_visible(False)
            # 判断目录是否存在，不存在则创建
            save_path = color_map_dto.image_path
            if os.path.exists(save_path) is False:
                os.makedirs(save_path)
            image_name = color_map_dto.image_name
            # 保存图片，tight让保存的图片更加紧促
            plt.savefig(save_path + image_name, bbox_inches='tight', transparent=True, pad_inches=0)
            plt.clf()
            plt.close(fig)
            common_response = com.CommonResponse(com.successCode, com.successMsg,
                                                 dict([("obj", save_path + image_name)]))
            log.info("svg出图执行完毕")
        except BaseException as e:
            print("执行svg出图失败：{}".format(e))
            common_response = com.CommonResponse(com.errorCode, "{}".format(e), dict([("obj", com.missingValues)]))
            log.error("svg出图执行异常：{}".format(e))
        finally:
            # 返回数据
            self.write(common_response.__dict__)
